/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pe.demo.ejemplodnie;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import sun.security.mscapi.SunMSCAPI;

/**
 *
 * @author alvaro
 */
public class FirmaDialog extends javax.swing.JDialog {

   /**
    * A return status code - returned if Cancel button has been pressed
    */
   public static final int RET_CANCEL = 0;
   /**
    * A return status code - returned if OK button has been pressed
    */
   public static final int RET_OK = 1;

   JList certsList = new JList();
   CertificateListModel certsListModel = new CertificateListModel();
   List<PrivateKey> privateKeyList = new ArrayList<PrivateKey>();
   PrivateKey selectedPrivateKey = null;

   /**
    * Creates new form FirmaDialog
    */
   public FirmaDialog(java.awt.Frame parent, boolean modal) throws
           KeyStoreException, RuntimeException, IOException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException, UnrecoverableEntryException {
      super(parent, modal);
      initComponents();

      certsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      certsList.setModel(certsListModel);
      certsList.setName("certificates");
      certsListModel.refresh();

      certsPanel.setViewportView(certsList);
      certsList.setCellRenderer(new DefaultListCellRenderer() {
         public Component getListCellRendererComponent(JList list, Object value, int index,
                 boolean isSelected, boolean cellHasFocus) {
                String subjectDN = getDescriptionFrom((X509Certificate) value);
                return super.getListCellRendererComponent(list, subjectDN, index, isSelected, cellHasFocus);
            }
        });

      certsList.addListSelectionListener(new ListSelectionListener() {
         @Override
         public void valueChanged(ListSelectionEvent e) {
            //setErrorText("");

            if (certsList.getSelectedValue() != null) {
               int id = certsList.getSelectedIndex();
               selectedPrivateKey = privateKeyList.get(id);
               okButton.setEnabled(true);
            } else {
               selectedPrivateKey = null;
               okButton.setEnabled(false);
            }
         }
      });

      // Close the dialog when Esc is pressed
      String cancelName = "cancel";
      InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
      ActionMap actionMap = getRootPane().getActionMap();
      actionMap.put(cancelName, new AbstractAction() {
         public void actionPerformed(ActionEvent e) {
            doClose(RET_CANCEL);
         }
      });
   }

   //public String getDescriptionFrom(PrivateKeyEntry value){
   //X509Certificate cert = (X509Certificate) value.getCertificate();
   public String getDescriptionFrom(X509Certificate cert) {
        String subjectDN = cert.getSubjectDN().getName();
        int dnStartIndex = subjectDN.indexOf("CN=") + 3;
        if (dnStartIndex >= 0 && subjectDN.indexOf(",", dnStartIndex) >= 0) {
            subjectDN = subjectDN.substring(dnStartIndex, subjectDN.indexOf(",", dnStartIndex));
        }
        return subjectDN;
   }

   /**
    * @return the return status of this dialog - one of RET_OK or RET_CANCEL
    */
   public int getReturnStatus() {
      return returnStatus;
   }

   /**
    * This method is called from within the constructor to initialize the form.
    * WARNING: Do NOT modify this code. The content of this method is always
    * regenerated by the Form Editor.
    */
   @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel4 = new javax.swing.JLabel();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        dataTextField = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        signatureTextArea = new javax.swing.JTextArea();
        certsPanel = new javax.swing.JScrollPane();
        jLabel3 = new javax.swing.JLabel();
        errorLabel = new javax.swing.JLabel();
        signInfoLabel = new javax.swing.JLabel();

        jLabel4.setText("jLabel4");

        setTitle("Ejemplo 2 DnieJava");
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                closeDialog(evt);
            }
        });

        okButton.setText("Firmar");
        okButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                okButtonActionPerformed(evt);
            }
        });

        cancelButton.setText("Cerrar");
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancelButtonActionPerformed(evt);
            }
        });

        jLabel1.setText("data:");

        jLabel2.setText("firma digital:");

        dataTextField.setText("holas a todos");

        signatureTextArea.setColumns(20);
        signatureTextArea.setLineWrap(true);
        signatureTextArea.setRows(5);
        jScrollPane1.setViewportView(signatureTextArea);

        jLabel3.setText("Certificados:");

        errorLabel.setText("l");

        signInfoLabel.setText("l");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(certsPanel)
                            .addComponent(jScrollPane1)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(dataTextField))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jLabel3)
                                    .addGroup(layout.createSequentialGroup()
                                        .addGap(187, 187, 187)
                                        .addComponent(cancelButton)))
                                .addGap(0, 0, Short.MAX_VALUE))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel2)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(signInfoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(194, 194, 194)
                        .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 184, Short.MAX_VALUE)))
                .addContainerGap())
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(certsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 96, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel1)
                    .addComponent(dataTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(okButton)
                    .addComponent(errorLabel))
                .addGap(11, 11, 11)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(signInfoLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE)
                .addGap(18, 18, 18)
                .addComponent(cancelButton)
                .addContainerGap())
        );

        getRootPane().setDefaultButton(okButton);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
       doClose(RET_OK);
    }//GEN-LAST:event_okButtonActionPerformed

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
       doClose(RET_CANCEL);
    }//GEN-LAST:event_cancelButtonActionPerformed

   /**
    * Closes the dialog
    */
    private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
       doClose(RET_CANCEL);
    }//GEN-LAST:event_closeDialog

   private void doClose(int retStatus) {
      returnStatus = retStatus;

      if (returnStatus == RET_OK) {
         try {
            if (selectedPrivateKey==null) return;
            if (dataTextField.getText().isEmpty()) return;

            Signature sig = Signature.getInstance("SHA1withRSA");
            sig.initSign(selectedPrivateKey);
            sig.update(dataTextField.getText().getBytes());

            byte[] signatureValue = sig.sign();
            String signatureString = bytArrayToHex(signatureValue);

            signatureTextArea.setText(signatureString);
            signInfoLabel.setText(signatureValue.length + " bytes, " +
                                  signatureString.length() + " chars");

         } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
         } catch (InvalidKeyException ex) {
            Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
         } catch (SignatureException ex) {
            Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
         }
      } else { // cerrar
         setVisible(false);
         dispose();
      }
   }

   String bytArrayToHex(byte[] a) {
      StringBuilder sb = new StringBuilder();
      for(byte b: a)
         sb.append(String.format("%02x", b&0xff));
      return sb.toString().toUpperCase();
   }

   /**
    * @param args the command line arguments
    */
   public static void main(String args[]) {
      /* Set the Nimbus look and feel */
      //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
       * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
       */
      try {
         for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Windows".equals(info.getName())) {
               javax.swing.UIManager.setLookAndFeel(info.getClassName());
               break;
            }
         }
      } catch (ClassNotFoundException ex) {
         java.util.logging.Logger.getLogger(FirmaDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (InstantiationException ex) {
         java.util.logging.Logger.getLogger(FirmaDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (IllegalAccessException ex) {
         java.util.logging.Logger.getLogger(FirmaDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (javax.swing.UnsupportedLookAndFeelException ex) {
         java.util.logging.Logger.getLogger(FirmaDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      }
      //</editor-fold>

      /* Create and display the dialog */
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            FirmaDialog dialog = null;
            try {
               dialog = new FirmaDialog(new javax.swing.JFrame(), true);
            } catch (KeyStoreException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (RuntimeException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchAlgorithmException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (CertificateException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (UnrecoverableKeyException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            } catch (UnrecoverableEntryException ex) {
               Logger.getLogger(FirmaDialog.class.getName()).log(Level.SEVERE, null, ex);
            }

            dialog.addWindowListener(new java.awt.event.WindowAdapter() {
               @Override
               public void windowClosing(java.awt.event.WindowEvent e) {
                  System.exit(0);
               }
            });
            dialog.setVisible(true);
         }
      });
   }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JScrollPane certsPanel;
    private javax.swing.JTextField dataTextField;
    private javax.swing.JLabel errorLabel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton okButton;
    private javax.swing.JLabel signInfoLabel;
    private javax.swing.JTextArea signatureTextArea;
    // End of variables declaration//GEN-END:variables
   private int returnStatus = RET_CANCEL;

   class CertificateListModel extends DefaultListModel {

      public void refresh() throws KeyStoreException, RuntimeException,
              IOException, NoSuchAlgorithmException, CertificateException,
              UnrecoverableKeyException, UnrecoverableEntryException {

        this.removeAllElements();

        SunMSCAPI providerMSCAPI = new SunMSCAPI();
        Security.addProvider(providerMSCAPI);
        KeyStore ks = KeyStore.getInstance("Windows-MY");
        ks.load(null,null);

        Enumeration<String> aliases = ks.aliases();
        while (aliases.hasMoreElements()) {
           String alias = aliases.nextElement();
           if (ks.isKeyEntry(alias)) {
              PrivateKey privKey = (PrivateKey) ks.getKey(alias, null);
              Certificate cert = ks.getCertificate(alias);
              this.addElement(cert);
              privateKeyList.add(privKey);
           }
        }

      }
   }
}
